import { FileUtils } from "../../../utils/FileUtils";
import { loadSync } from "protobufjs";
import { join } from "path";
import _ = require("lodash");
import { IProtocol } from "./IProtocol";
import { IProtocolData } from "./IProtocolData";
import { BaseResp } from "../../../entity/BaseEntity";

export class ProtobufProtocol implements IProtocol {
    private _proto = null;
    loadProtoDir(dirPath: string) {
        let protoFiles = FileUtils.readFile(dirPath, "proto");
        protoFiles = protoFiles.map(fileName => join(dirPath, fileName))
        this._proto = loadSync(protoFiles).nested;
    }

    decode(buff: Buffer): IProtocolData {
        if (buff.length == 5 && buff.toString() == this.heartData) return null;
        var msgSize = buff.readUInt32LE(0);
        var id: number = buff.readUInt32LE(4);
        var code: number = buff.readUInt8(8);
        var cmdSize: number = buff.readUInt16LE(9);
        var cmd: string = buff.toString("utf8", 11, 11 + cmdSize);
        let model = this.lookup(cmd + "Request");
        if (!model) {
            throw new TypeError(`${cmd} not found, please check it again`)
        }
        var data = model.decode(buff.slice(11 + cmdSize));
        return { cmd: cmd, id: id, data: data };
    }

    encode(cmd: string, data: BaseResp, id?: number): Buffer | string {
        let model = this.lookup(cmd + "Response");
        if (!model) {
            throw new TypeError(`${cmd} not found, please check it again`)
        }
        // let msgObj = model.create(params)
        var buffer: Buffer = model.encode(data.data).finish();
        var head = Buffer.alloc(11 + cmd.length);
        head.writeUInt32LE(id, 4);
        head.writeUInt8(data.code, 8);
        head.writeUInt16LE(cmd.length, 9);
        head.write(cmd, 11, "utf8");
        head.writeUInt32LE(head.length + buffer.length - 4, 0);
        return Buffer.concat([head, buffer]);
    }

    get heartData() { return "heart"; }

    private lookup(protoName: string) {
        if (!this._proto) {
            throw new TypeError('Please load proto before lookup')
        }
        return _.get(this._proto, protoName)
    }
}
